
#include <BStream>
#include "member_BXmlDocument.h"
#include "member_BXmlNode.h"

using namespace BWE;

member_BXmlDocument::member_BXmlDocument(BXmlDocument* xmldocument)
{
	boss = xmldocument;
	root = 0;
}
member_BXmlDocument::~member_BXmlDocument()
{

}

int member_BXmlDocument::readTagName(const BString& text, BString& tagName)
{
	int begin = 1;
	while (begin < text.size())
	{
		if (isalpha(text[begin]))
			break;
		begin++;
	}
	int end = begin;
	while (end < text.size())
	{
		if (isspace(text[end]))
			break;
		if (text[end] == '/')
			break;
		if (text[end] == '>')
			break;
		end++;
	}
	tagName.reset(text, begin, end);
	return end;
}

bool member_BXmlDocument::overTest(const char* name, BXmlNode* node)
{
	if (name[0] == '/' && node->name() == name + 1)
	{
		return true;
	}
	return false;
}

bool member_BXmlDocument::readNode(BXmlNode* node, const BString& xmltext, int &pos)
{
	BString text;
	char linebreak = 0;
	int comm_begin = -1;
	bool quot_begin = false;
	int tag_head = -1;
	int tag_tail = -1;
	char code = 0;
	while (pos < xmltext.size())
	{
		code = xmltext[pos];
		text.append(code);
		pos++;
		if (code == '\n')
			linebreak = '\n';
		if (comm_begin >= 0)
		{
			if (code == '>' && text.endWith("-->"))
			{
				BString comment;
				text.substr(comment, comm_begin + 4, text.size() - 3);

				BXmlNode* commentNode = new BXmlNode('!', node);
				commentNode->setText(comment);
				xml_member(commentNode)->format.head_prev = linebreak;
				linebreak = 0;

				text.remove(comm_begin, text.size());
				text.trimmed();
				if (text.size() && text != ' ')
				{
					if (node->childCount() == 0)
						node->setText(text);
					else
						node->addText(text);
				}
				comm_begin = -1;
			}
			continue;
		}
		if (code == '-' && text.endWith("<!--"))
		{
			comm_begin = text.size() - 4;
			continue;
		}
		if (quot_begin)
		{
			if (code == '"')
			{
				quot_begin = false;
			}
			continue;
		}
		if (code == '"')
		{
			quot_begin = true;
			continue;
		}
		if (code == '<')
		{
			tag_head = text.size() - 1;
		}
		if (code == '>')
		{
			tag_tail = text.size();
		}
		if (tag_tail > tag_head)
		{
			BString tagText;
			text.substr(tagText, tag_head, tag_tail);

			BString tagName;
			int name_end = readTagName(tagText, tagName);

			if (tagText.beginWith("</"))
			{
				const BString& name = node->name();
				if (tagName != name)
					return false;
				xml_member(node)->format.tail_prev = linebreak;
				linebreak = 0;
				text.remove(tag_head, tag_tail);
				text.trimmed();
				if (text.size() && text != ' ')
				{
					if (node->childCount() == 0)
					{
						node->setText(text);
						xml_member(node)->format.head_post = linebreak;
						linebreak = 0;
					}
					else
					{
						node->addText(text);
					}
					text.reset();
				}
				break;
			}
			if (tagText.endWith("/>"))
			{
				BXmlNode* child = new BXmlNode(tagName, node);
				xml_member(child)->format.head_prev = linebreak;
				linebreak = 0;
				tagText -= "/>";
				tagText.remove(0, name_end);
				readAttribs(child, tagText);

				text.remove(tag_head, tag_tail);
				text.simplify();
				if (text.size() && text != ' ')
				{
					if (node->childCount() == 0)
					{
						node->setText(text);
						xml_member(child)->format.head_post = linebreak;
						linebreak = 0;
					}
					else
						node->addText(text);
					text.reset();
				}
			}
			else
			{
				BXmlNodeHolder child = new BXmlNode(tagName);
				tagText -= '>';
				tagText.remove(0, name_end);
				readAttribs(child, tagText);
				int old_pos = pos;
				if (readNode(child, xmltext, pos))
				{
					text.remove(tag_head, tag_tail);
					text.simplify();
					if (text.size() && text != ' ')
					{
						if (node->childCount() == 0)
						{
							node->setText(text);
							xml_member(child)->format.head_post = linebreak;
							linebreak = 0;
						}
						else
							node->addText(text);
					}
					text.reset();

					node->addChild(child);
					xml_member(child)->format.head_prev = linebreak;
					linebreak = 0;
				}
				else
				{
					pos = old_pos;
				}
			}
			tag_head = -1;
			tag_tail = -1;
		}
	}

	return true;
}
void member_BXmlDocument::readAttribs(BXmlNode* node, BString& text)
{
	if (text.empty())
		return;

	text.simplify();
	
	BString label;
	BString value;

	int begin = -1;
	int end = 0;
	while (end < text.size())
	{
		if (text[end] == '\"')
		{
			if (begin < 0)
			{
				begin = end++;
				continue;
			}
			text.substr(value, begin + 1, end);
			text.substr(label, 0, begin);
			label.trimmed();
			if (label.last() != '=')
				return;
			label.remove('=');
			label.trimmed();
			node->setAttrib(label, value);
			text.remove(0, end + 1);
			begin = -1;
			end = 0;
		}
		end++;
	}
}
void member_BXmlDocument::makeFormat(BXmlNode* node)
{
	member_BXmlNode* mem = xml_member(node);
	mem->format.toString();

	BArray<BXmlNodeHolder>& children = mem->children;
	for (int i = 0; i < children.size(); i++)
	{
		BXmlNode* child = children[i];
		makeFormat(child);
	}
}

const XmlFormat* member_BXmlDocument::nodeFormat(const BXmlNode* node)
{
	member_BXmlNode* sonmem = xml_member(node);
	if (!sonmem->format.text.empty())
		return &sonmem->format;
	return &format;
}
void member_BXmlDocument::writeNode(const BXmlNode* node, BString& xmltext, int tab)
{
	const XmlFormat* fmt = nodeFormat(node);
	member_BXmlNode* mem = xml_member(node);
	if (fmt->head_prev && !xmltext.endWith('\n'))
		xmltext << '\n';
	if (xmltext.endWith('\n'))
		xmltext.append('\t', tab);
	if (node->name().empty())
	{
		if (mem->text.size())
			xmltext << mem->text;
		if (fmt->tail_post)
			xmltext << '\n';
		return;
	}
	if (node->name() == "!")
	{
		xmltext << "<!--";
		xmltext << node->text();
		xmltext << "-->";
		if (fmt->tail_post)
			xmltext << '\n';
		return;
	}
	xmltext << '<' << node->name();
	if (mem->attribSet.size())
	{
		for (auto it = mem->attribSet.begin(); it.valid(); ++it)
		{
			xmltext << ' ' << it.key() << '=' << '\"' << it.value() << '\"';
		}
	}
	if (mem->text.empty() && mem->children.empty())
	{
		xmltext << "/>";
		if (fmt->tail_post)
			xmltext << '\n';
		return;
	}
	xmltext << '>';
	if (fmt->head_post)
		xmltext << '\n';
	if (mem->text.size())
	{
		if (fmt->text_prev)
		{
			int pos = 0;
			while (pos < mem->text.size())
			{
				if (!xmltext.endWith('\n'))
					xmltext << '\n';
				int end = mem->text.find('\n', pos);
				xmltext.append('\t', tab + 1);
				xmltext.append(mem->text.cstr(), pos, end);
				pos = end + 1;
			}
		}
		else
		{
			if (!xmltext.endWith('\n'))
				xmltext.append('\t', tab + 1);
			xmltext << mem->text;
		}
		if (fmt->text_post)
		{
			xmltext << '\n';
		}
	}
	if (mem->children.size())
	{
		for (int i = 0; i < node->childCount(); i++)
		{
			const BXmlNode* child = node->child(i);
			writeNode(child, xmltext, tab + 1);
		}
	}
	if (fmt->tail_prev && !xmltext.endWith('\n'))
		xmltext << '\n';
	if (xmltext.endWith('\n'))
		xmltext.append('\t', tab);
	xmltext << "</" << node->name() << ">";
	if (fmt->tail_post)
		xmltext << '\n';
}
